﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sunny.Controls
{
	public class Table : System.Windows.Controls.Border
	{
		private System.Windows.Controls.Grid _Grid;
		protected System.Windows.Controls.Grid Grid
		{
			get
			{
				if (this._Grid == null)
				{
					this._Grid = new System.Windows.Controls.Grid();
				}
				return this._Grid;
			}
		}

		private System.Collections.Generic.List<TableColumn> _Columns;
		public System.Collections.Generic.List<TableColumn> Columns
		{
			get
			{
				if (this._Columns == null)
					this._Columns = new List<TableColumn>();
				return this._Columns;
			}
		}

		private System.Collections.ObjectModel.ObservableCollection<TableRow> _Rows;
		public System.Collections.ObjectModel.ObservableCollection<TableRow> Rows
		{
			get
			{
				if (this._Rows == null)
				{
					this._Rows = new System.Collections.ObjectModel.ObservableCollection<TableRow>();
					this._Rows.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Rows_CollectionChanged);
				}
				return this._Rows;
			}
		}

		private System.Windows.Style _CellStyle;
		protected System.Windows.Style CellStyle
		{
			get
			{
				if (this._CellStyle == null)
				{
					this._CellStyle = new System.Windows.Style();
					this._CellStyle.TargetType = typeof(TableCell);
					this._CellStyle.Setters.Add(this.CellPaddingSetter);
					this._CellStyle.Setters.Add(this.BorderBrushSetter);
					this._CellStyle.Setters.Add(this.BorderThicknessSetter);
				}
				return this._CellStyle;
			}
		}

		private System.Windows.Setter _CellPaddingSetter;
		protected System.Windows.Setter CellPaddingSetter
		{
			get
			{
				if (this._CellPaddingSetter == null)
				{
					this._CellPaddingSetter = new System.Windows.Setter();
					this._CellPaddingSetter.Property = TableCell.PaddingProperty;
					this._CellPaddingSetter.Value = new System.Windows.Thickness();
				}
				return this._CellPaddingSetter;
			}
		}

		/// <summary>
		/// The padding of all the cells in the table.
		/// </summary>
		[System.ComponentModel.DefaultValue(typeof(System.Windows.Thickness), "0")]
		public System.Windows.Thickness CellPadding
		{
			get
			{
				return (System.Windows.Thickness)this.CellPaddingSetter.Value;
			}
			set
			{
				this.CellPaddingSetter.Value = value;
			}
		}

		private System.Windows.Setter _BorderBrushSetter;
		protected System.Windows.Setter BorderBrushSetter
		{
			get
			{
				if (this._BorderBrushSetter == null)
				{
					this._BorderBrushSetter = new System.Windows.Setter();
					this._BorderBrushSetter.Property = TableCell.BorderBrushProperty;
					this._BorderBrushSetter.Value = this.BorderBrush;
				}
				return this._BorderBrushSetter;
			}
		}

		/// <summary>
		/// The color of all the cells' borders in the table.
		/// </summary>
		[System.ComponentModel.DefaultValue(typeof(System.Windows.Media.Brush),"Black")]
		public System.Windows.Media.Brush BorderColor
		{
			get
			{
				return (System.Windows.Media.Brush)this.BorderBrushSetter.Value;
			}
			set
			{
				this.BorderBrushSetter.Value = value;
				this.BorderBrush = value;
			}
		}

		private System.Windows.Setter _BorderThicknessSetter;
		protected System.Windows.Setter BorderThicknessSetter
		{
			get
			{
				if (this._BorderThicknessSetter == null)
				{
					this._BorderThicknessSetter = new System.Windows.Setter();
					this._BorderThicknessSetter.Property = TableCell.BorderThicknessProperty;
					this._BorderThicknessSetter.Value = new System.Windows.Thickness(0, 0, 1, 1);
				}
				return this._BorderThicknessSetter;
			}
		}

		/// <summary>
		/// The font size of all the cells  in the table.
		/// </summary>
		[System.ComponentModel.DefaultValue(12)]
		public double FontSize
		{
			get
			{
				return (double)this.FontSizeSetter.Value;
			}
			set
			{
				if (this.CellStyle.Setters.Contains(this.FontSizeSetter))
					this.CellStyle.Setters.Remove(this.FontSizeSetter);
				this.FontSizeSetter.Value = value;
				this.CellStyle.Setters.Add(this.FontSizeSetter);
			}
		}

		private System.Windows.Setter _FontSizeSetter;
		protected System.Windows.Setter FontSizeSetter
		{
			get
			{
				if (this._FontSizeSetter == null)
				{
					this._FontSizeSetter = new System.Windows.Setter();
					this._FontSizeSetter.Property = TableCell.FontSizeProperty;
					this._FontSizeSetter.Value = (double)12;
				}
				return this._FontSizeSetter;
			}
		}

		/// <summary>
		/// The font family of all the cells  in the table.
		/// </summary>
		public System.Windows.Media.FontFamily FontFamily
		{
			get
			{
				return (System.Windows.Media.FontFamily)this.FontFamilySetter.Value;
			}
			set
			{
				if (this.CellStyle.Setters.Contains(this.FontFamilySetter))
					this.CellStyle.Setters.Remove(this.FontFamilySetter);
				this.FontFamilySetter.Value = value;
				this.CellStyle.Setters.Add(this.FontFamilySetter);
			}
		}

		private System.Windows.Setter _FontFamilySetter;
		protected System.Windows.Setter FontFamilySetter
		{
			get
			{
				if (this._FontFamilySetter == null)
				{
					this._FontFamilySetter = new System.Windows.Setter();
					this._FontFamilySetter.Property = TableCell.FontFamilyProperty;
					this._FontFamilySetter.Value = new System.Windows.Media.FontFamily();
				}
				return this._FontFamilySetter;
			}
		}

		/// <summary>
		/// The borders' Thickness of the cells in the table.
		/// </summary>
		[System.ComponentModel.DefaultValue(1)]
		public int BorderWidth
		{
			get
			{
				return (int)this.BorderThickness.Left;
			}
			set
			{
				this.BorderThicknessSetter.Value = new System.Windows.Thickness(0, 0, value, value);
				this.BorderThickness = new System.Windows.Thickness(value, value, 0, 0);
			}
		}

		public Table()
		{
			this.BorderBrush = System.Windows.Media.Brushes.Black;
			this.BorderThickness = new System.Windows.Thickness(1, 1, 0, 0);

			//this.Style.TargetType = this.GetType();
			//this.Style.Resources.Add(this.PaddingSetter);
			this.Resources.Add("TableCellsDefaultStyle", this.CellStyle);

			this.Child = this.Grid;
			//Common.SynchronizeCollection(this.Rows, this.Grid.Children);
		}

		void Rows_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
				if (e.OldItems!= null)
					foreach (TableRow row in e.OldItems)
						if (this.Rows.IndexOf(row) < 0)
							foreach (TableCell cell in row.Cells)
								this.Grid.Children.Remove(cell);

			//for (int i = this.Grid.RowDefinitions.Count; i < this.Rows.Count; i++)
			//	this.Grid.RowDefinitions.Add(this.Rows[i]);

			//for (int i = this.Grid.RowDefinitions.Count - 1; i >= this.Rows.Count; i--)
			//	this.Grid.RowDefinitions.RemoveAt(i);

			if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add || e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace && e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
				if (e.NewItems != null)
					foreach (TableRow row in e.NewItems)
					{
						row.Cells.CollectionChanged -= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Cells_CollectionChanged);
						row.Cells.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Cells_CollectionChanged);
						this.Cells_CollectionChanged(row.Cells, new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, row.Cells));
					}

			this.Grid.RowDefinitions.Clear();
			foreach (TableRow row in this.Rows)
				this.Grid.RowDefinitions.Add(row);
		}

		void Cells_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.NewItems == null)
				return;

			switch (e.Action)
			{
				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
					foreach (TableCell cell in e.NewItems)
						this.Grid.Children.Add(cell);
					break;
				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
					foreach (TableCell cell in e.NewItems)
						this.Grid.Children.Remove(cell);
					break;
				case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
					for (int i = 0; i < e.NewItems.Count; i++)
						this.Grid.Children[this.Grid.Children.IndexOf((TableCell)e.OldItems[i])] = (TableCell)e.NewItems[i];
					break;
				case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
					foreach (TableCell cell in e.OldItems)
						this.Grid.Children.Remove(cell);
					foreach (TableCell cell in e.NewItems)
						this.Grid.Children.Add(cell);
					break;
			}
		}

		protected override void OnRender(System.Windows.Media.DrawingContext dc)
		{
			//this.InitializeColumns();
			this.InitializeCells();

			base.OnRender(dc);

			//string savedButton = System.Windows.Markup.XamlWriter.Save(this);
		}

		//public int ColumnCount
		//{
		//    get
		//    {
		//        return this.Rows.Max(r => r.Cells.Sum(c => c.ColumnSpan));
		//    }
		//}

		protected void InitializeColumns(int columnCount)
		{
			if (this.Rows.Count == 0)
				return;

			System.Collections.Generic.IEnumerable<TableCell> cells = this.Rows.SelectMany(r => r.Cells);

			//for (int i = this.Grid.ColumnDefinitions.Count - 1; i >= columnCount; i--)
			//    this.Grid.ColumnDefinitions.RemoveAt(i);
			this.Grid.ColumnDefinitions.Clear();

			for (int i = 0; i < columnCount; i++)
			{
				if (i == this.Grid.ColumnDefinitions.Count)
					this.Grid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition());
				if (this.Columns.Count > i)
				{
					TableColumn column = this.Columns[i];
					this.Grid.ColumnDefinitions[i] = column;
					string styleKey = string.Format("TableColumn{0}Style", i);
					if (this.Resources.Contains(styleKey))
						this.Resources.Remove(styleKey);
					if (this.Resources.Contains(column.ColumnStyle))
						this.Resources.Remove(column.ColumnStyle);
					if (column.ColumnStyle.BasedOn == null)
						column.ColumnStyle.BasedOn = this.CellStyle;
					this.Resources.Add(styleKey, column.ColumnStyle);
					foreach (TableCell cell in cells.Where(c => c.ColumnIndex == i))
						cell.Style = column.ColumnStyle; 
				}	
				//else this.Grid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefisanition());
			}

			//foreach (TableRow row in this.Rows)
			//    System.Windows.Controls.Grid.SetColumnSpan(row, columnCount);
		}

		protected void InitializeCells()
		{
			//int columnCount = this.ColumnCount;
			//TableCell[,] cells = new TableCell[this.Rows.Count, columnCount];
			System.Collections.Generic.List<System.Collections.Generic.List<TableCell>> rows = new List<List<TableCell>>();

			for (int i = 0; i < this.Rows.Count; i++)
			{
				TableRow row = this.Rows[i];
				for (int j = 0; j < row.Cells.Count; j++)
				{
					TableCell cell = row.Cells[j];
					int columnIndex = j - 1;

					do
					{
						this.FillCell(rows, i, ++columnIndex);
					}
					while (rows[i][columnIndex] != null);

					//cells[i, columnIndex] = cell;
					cell.RowIndex = i;
					cell.ColumnIndex = columnIndex;

					for (int rowIndex = cell.RowIndex; rowIndex < cell.RowIndex + cell.RowSpan; rowIndex++)
						for (columnIndex = cell.ColumnIndex; columnIndex < cell.ColumnIndex + cell.ColumnSpan; columnIndex++)
						{
							this.FillCell(rows, rowIndex, columnIndex);
							rows[rowIndex][columnIndex] = cell;
						}
				}
			}

			this.InitializeColumns(rows.Max(cells => cells.Count));
		}

		protected void FillCell(System.Collections.Generic.List<System.Collections.Generic.List<TableCell>> rows, int rowIndex, int columnIndex)
		{
			for (int i = 0; i <= rowIndex; i++)
			{
				if (rows.Count == i)
					rows.Add(new List<TableCell>());
				List<TableCell> cells = rows[i];
				for (int j = 0; j <= columnIndex; j++)
					if (cells.Count == j)
						cells.Add(null);
			}
		}

		//protected System.Collections.Generic.IEnumerable<TableCell> GetFlowCells(System.Collections.Generic.IEnumerable<TableCell>cells,TableCell cell)
		//{
		//    return cells.Where(c => c.RowIndex >= cell.RowIndex && c.RowIndex < cell.RowIndex + cell.RowSpan && c.ColumnIndex > cell.ColumnIndex && c != cell);
		//}

		//protected void AddColumnIndex(TableCell startCell, int iStart, int jStart)
		//{
		//    for (int i = iStart; i < this.Rows.Count; i++)
		//    {
		//        TableRow row = this.Rows[i];
		//        for (int j = jStart; j < row.Cells.Count; j++)
		//        {
		//            TableCell cell = row.Cells[j];
		//            if (cell == startCell)
		//                continue;
		//            if (cell.AdditionalColumnIndex + j >= startCell.AdditionalColumnIndex + jStart && i < iStart + startCell.RowSpan)
		//                cell.AdditionalColumnIndex += startCell.ColumnSpan - 1;
		//        }
		//    }
		//}
	}
}